﻿#region

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.ServiceModel.DomainServices.Client;
using System.Windows;
using System.Windows.Media;
using NetBay.Core.BusinessEntities;
using NetBay.Core.BusinessEntities.Historic;
using Rio.Applications.Web.Services;
using Rio.Framework.Pages.ChildWindows;
using Rio.Framework.Pages.Events;

#endregion

namespace Rio.Framework.Pages.CriteriaControls
{
    public partial class StackIndexControl
    {
        #region Delegates

        public delegate void ChronoNumberGeneratedEventHandler(object sender, ChronoNumberGeneratedEventArgs args);

        #endregion

        #region Readonly & Static Fields

        public static readonly DependencyProperty CurrentFolderProperty =
            DependencyProperty.Register("CurrentFolder", typeof (Folder), typeof (StackIndexControl), new PropertyMetadata(OnFolderChanged));

        public static readonly DependencyProperty CurrentFolderTypeProperty =
            DependencyProperty.Register("CurrentFolderType", typeof (FolderType), typeof (StackIndexControl), new PropertyMetadata(OnFolderTypeChanged));

        public static readonly DependencyProperty CurrentOrganizationProperty =
            DependencyProperty.Register("CurrentOrganization", typeof (Organization), typeof (StackIndexControl), new PropertyMetadata(OnOrganizationChanged));

        public static readonly DependencyProperty UseChronoGenerationProperty =
            DependencyProperty.Register("UseChronoGeneration", typeof (bool), typeof (StackIndexControl), null);

        private readonly List<CriteriaControlBase> _internalControlsList;

        #endregion

        #region Fields

        private List<Index> _internalListIndex;
        private bool _isFilterEnabled;

        #endregion

        #region Constructors

        /// <summary>
        ///   Initializes a new instance of the <see cref="StackIndexControl" /> class.
        /// </summary>
        public StackIndexControl()
        {
            InitializeComponent();

            _internalControlsList = new List<CriteriaControlBase>();
            _internalListIndex = new List<Index>();
        }

        /// <summary>
        ///   Initializes a new instance of the <see cref="StackIndexControl" /> class.
        /// </summary>
        /// <param name="folderType"> Type of the folder. </param>
        public StackIndexControl(FolderType folderType)
            : this()
        {
            CurrentFolderType = folderType;
        }

        #endregion

        #region Instance Indexers

        /// <summary>
        ///   Gets the <see cref="string" /> at the specified index.
        /// </summary>
        /// <value> </value>
        [IndexerName("Values")]
        public string this[int index]
        {
            get
            {
                return GetIndexValueByRank(index);
            }
            set
            {
                SetIndexValueByRank(index, value);
            }
        }

        #endregion

        #region Instance Properties

        /// <summary>
        ///   Gets the criteria controls.
        /// </summary>
        /// <value> The criteria controls. </value>
        public List<CriteriaControlBase> CriteriaControls
        {
            get
            {
                return _internalControlsList;
            }
        }

        /// <summary>
        ///   Gets or sets the current folder.
        /// </summary>
        /// <value> The current folder. </value>
        public Folder CurrentFolder
        {
            get
            {
                return (Folder) GetValue(CurrentFolderProperty);
            }
            set
            {
                SetValue(CurrentFolderProperty, value);
            }
        }

        /// <summary>
        ///   Gets or sets the type of the current folder.
        /// </summary>
        /// <value> The type of the current folder. </value>
        public FolderType CurrentFolderType
        {
            get
            {
                return (FolderType) GetValue(CurrentFolderTypeProperty);
            }
            set
            {
                SetValue(CurrentFolderTypeProperty, value);
            }
        }

        /// <summary>
        ///   Gets or sets the current organization.
        /// </summary>
        /// <value> The current organization. </value>
        public Organization CurrentOrganization
        {
            get
            {
                return (Organization) GetValue(CurrentOrganizationProperty);
            }
            set
            {
                SetValue(CurrentOrganizationProperty, value);
            }
        }

        /// <summary>
        ///   Gets or sets the is search mode.
        /// </summary>
        /// <value> The is search mode. </value>
        public bool IsSearchMode { get; set; }

        /// <summary>
        ///   Gets or sets a value indicating whether [use chrono generation].
        /// </summary>
        /// <value> <c>true</c> if [use chrono generation]; otherwise, <c>false</c> . </value>
        public bool UseChronoGeneration
        {
            get
            {
                return (bool) GetValue(UseChronoGenerationProperty);
            }
            set
            {
                SetValue(UseChronoGenerationProperty, value);
            }
        }

        #endregion

        #region Instance Methods

        /// <summary>
        ///   Changes the color of the border.
        /// </summary>
        /// <param name="indexList"> The index list. </param>
        /// <param name="color"> The color. </param>
        public void ChangeBorderColor(List<long> indexList, Color color)
        {
            foreach (CriteriaControlBase item in _internalControlsList)
            {
                for (int j = 0; j < indexList.Count; j++)
                {
                    if (item.CurrentIndex.Indentifier == indexList[j])
                    {
                        item.ChangeBorderColor(color);
                    }
                }
            }
        }

        /// <summary>
        ///   Remove all <cref>CriteriaControlBase</cref> controls
        /// </summary>
        public void Clear()
        {
            _internalControlsList.Clear();
            StackFolderControl.Children.Clear();
        }

        /// <summary>
        ///   Clear criteria values
        /// </summary>
        public void ClearCriterias()
        {
            foreach (CriteriaControlBase item in StackFolderControl.Children)
            {
                item.Clear();
            }
        }

        /// <summary>
        ///   Gets the index.
        /// </summary>
        /// <returns> Returns a list of all index </returns>
        public List<Index> GetAllIndex()
        {
            return _internalControlsList.Select(item => item.CurrentIndex).ToList();
        }

        /// <summary>
        ///   Gets all inputed values.
        /// </summary>
        /// <returns> Returns a dictionary of index ids and text values </returns>
        public Dictionary<long, string> GetIndexIdValues()
        {
            return _internalControlsList.ToDictionary(item => item.CurrentIndex.Indentifier, item => item.InputValue);
        }

        /// <summary>
        ///   Gets the index id values to search.
        /// </summary>
        /// <returns> </returns>
        public Dictionary<long, string> GetIndexIdValuesToSearch()
        {
            return _internalControlsList.ToDictionary(item => item.CurrentIndex.Indentifier, item => item.InputSearchValue);
        }

        /// <summary>
        ///   Gets the index name values.
        /// </summary>
        /// <returns> Returns a dictionary of index name and text values </returns>
        public Dictionary<string, string> GetIndexNameValues()
        {
            return _internalControlsList.ToDictionary(item => item.CurrentIndex.Name, item => item.InputValue);
        }

        /// <summary>
        ///   Gets the index name values to search.
        /// </summary>
        /// <returns> </returns>
        public Dictionary<string, string> GetIndexNameValuesToSearch()
        {
            return _internalControlsList.ToDictionary(item => item.CurrentIndex.Name, item => item.InputSearchValue);
        }

        /// <summary>
        ///   Gets the index value by id.
        /// </summary>
        /// <param name="indexIdentifier"> The index identifier. </param>
        /// <returns> </returns>
        public string GetIndexValueById(long indexIdentifier)
        {
            var ind = _internalControlsList.SingleOrDefault(i => i.CurrentIndex.Indentifier == indexIdentifier);
            return ind != null ? ind.InputValue : string.Empty;
        }

        /// <summary>
        ///   Gets the index value by rank.
        /// </summary>
        /// <param name="rank"> The rank. </param>
        /// <returns> </returns>
        public string GetIndexValueByRank(int rank)
        {
            var ind = _internalControlsList[rank];
            return ind != null ? ind.InputValue : string.Empty;
        }

        /// <summary>
        ///   Gets the index values.
        /// </summary>
        /// <returns> Returns a dictionary of index and text values </returns>
        public Dictionary<Index, string> GetIndexValues()
        {
            return _internalControlsList.ToDictionary(item => item.CurrentIndex, item => item.InputValue);
        }

        /// <summary>
        ///   Gets the index values to search.
        /// </summary>
        /// <returns> </returns>
        public Dictionary<Index, string> GetIndexValuesToSearch()
        {
            return _internalControlsList.ToDictionary(item => item.CurrentIndex, item => item.InputSearchValue);
        }

        /// <summary>
        ///   Gets the list index for historical.
        /// </summary>
        /// <returns> </returns>
        public List<HistoricIndex> GetListIndexForHistorical()
        {
            var items = GetIndexValues();
            return items.Select(item => new HistoricIndex
                                            {
                                                IndexId = item.Key.Indentifier,
                                                IndexLabel = item.Key.IndexLabel,
                                                IndexValue = item.Value
                                            }).ToList();
        }

        /// <summary>
        ///   Gets the unique index values.
        /// </summary>
        /// <returns> </returns>
        public Dictionary<long, string> GetUniqueIndexValues()
        {
            Dictionary<long, string> indexIdsValues = new Dictionary<long, string>();
            foreach (CriteriaControlBase item in _internalControlsList)
            {
                if (item.CurrentIndex.IsUnique && !String.IsNullOrWhiteSpace(item.InputValue))
                {
                    indexIdsValues.Add(item.CurrentIndex.Indentifier, item.InputValue);
                }
            }
            return indexIdsValues;
        }

        /// <summary>
        ///   Hides all filter.
        /// </summary>
        public void HideFilters()
        {
            foreach (CriteriaControlBase item in _internalControlsList)
            {
                item.DisableFilter();
            }
        }

        /// <summary>
        ///   Initializes the control.
        /// </summary>
        /// <param name="organization"> The organization. </param>
        /// <param name="folderType"> Type of the folder. </param>
        /// <param name="values"> The values. </param>
        public void InitializeControl(Organization organization, FolderType folderType, List<string> values = null)
        {
            if (organization != null && folderType != null)
            {
                CurrentFolderType = folderType;
                CurrentOrganization = organization;
                _internalListIndex = new List<Index>(CurrentFolderType.LstIndex);

                if (values == null)
                    BuildControlCriterias();
                else
                    BuildControlCriteriasWithValue(values);
            }
        }

        /// <summary>
        ///   Initializes the index of the control with.
        /// </summary>
        /// <param name="organization"> The organization. </param>
        /// <param name="folderType"> Type of the folder. </param>
        /// <param name="index"> The index. </param>
        public void InitializeControlWithIndex(Organization organization, FolderType folderType, List<Index> index)
        {
            CurrentFolderType = folderType;
            CurrentOrganization = organization;
            _internalListIndex = new List<Index>(index);

            BuildControlCriterias();
        }

        /// <summary>
        ///   Determines whether [is mandatory index satified].
        /// </summary>
        /// <returns> </returns>
        public bool IsMandatoryIndexSatisfied()
        {
            foreach (CriteriaControlBase item in _internalControlsList)
            {
                if (item.CurrentIndex.IsMandatory)
                {
                    if (String.IsNullOrWhiteSpace(item.InputValue))
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        public void RaiseChronoNumberGeneratedEvent(ChronoNumberGeneratedEventArgs args)
        {
            if (ChronoNumberGenerated != null)
                ChronoNumberGenerated(this, args);
        }

        /// <summary>
        ///   Rebinds this instance.
        /// </summary>
        public void Rebind()
        {
            InitializeControlWithFolderInternal();
        }

        /// <summary>
        ///   Removes the chrono.
        /// </summary>
        /// <returns> </returns>
        public bool RemoveChrono()
        {
            foreach (CriteriaControlBase item in _internalControlsList)
            {
                if (item.Equals(typeof (TextIndexControl)))
                {
                    Index L_oIndex = item.CurrentIndex;
                    if (L_oIndex.Type == (int) IndexDomainServiceEnums.IndexType.TypeChrono)
                    {
                        item.InputValue = String.Empty;
                        return true;
                    }
                }
            }
            return false;
        }

        public void RevertBorderColor()
        {
            foreach (CriteriaControlBase item in _internalControlsList)
            {
                item.RevertBorderColor();
            }
        }

        /// <summary>
        ///   Sets the index value by id.
        /// </summary>
        /// <param name="indexIdentifier"> The index identifier. </param>
        /// <param name="value"> The value. </param>
        public void SetIndexValueById(long indexIdentifier, string value)
        {
            var ind = _internalControlsList.SingleOrDefault(i => i.CurrentIndex.Indentifier == indexIdentifier);
            if (ind != null)
            {
                if (ind is ListOjectIndexControl)
                {
                    ind.SelectedValueAfterInit = value;
                }
                else if (ind is ListUserIndexControl)
                {
                    ind.SelectedValueAfterInit = value;
                }
                else
                {
                    ind.InputValue = value;
                }
            }
        }

        /// <summary>
        ///   Sets the index value by rank.
        /// </summary>
        /// <param name="rank"> The rank. </param>
        /// <param name="value"> The value. </param>
        public void SetIndexValueByRank(int rank, string value)
        {
            var ind = _internalControlsList[rank];
            if (ind != null)
            {
                if (ind is ListOjectIndexControl)
                {
                    ind.SelectedValueAfterInit = value;
                }
                else if (ind is ListUserIndexControl)
                {
                    ind.SelectedValueAfterInit = value;
                }
                else
                {
                    ind.InputValue = value;
                }
            }
        }

        /// <summary>
        ///   Gets all inputed values.
        /// </summary>
        /// <returns> Returns a dictionary of index ids and text values </returns>
        public void SetIndexValues(Dictionary<long, string> indexIdsValues)
        {
            if (indexIdsValues != null && indexIdsValues.Count > 0)
            {
                foreach (KeyValuePair<long, string> item in indexIdsValues)
                {
                    SetIndexValueById(item.Key, item.Value);
                }
            }
        }

        /// <summary>
        ///   Sets the index values.
        /// </summary>
        /// <param name="values"> The values. </param>
        public void SetIndexValues(List<string> values)
        {
            if (values != null && values.Count > 0)
            {
                for (int i = 0; i < _internalControlsList.Count; i++)
                {
                    SetIndexValueByRank(i, values.ElementAt(i));
                }
            }
        }

        protected void OnIndexLoaded()
        {
            if (IndexLoaded != null)
                IndexLoaded(this, new EventArgs());
        }

        /// <summary>
        ///   Builds the control criterias.
        /// </summary>
        private void BuildControlCriterias()
        {
            Clear();

            //Build and add each controls on the stack
            foreach (Index ind in _internalListIndex)
            {
                CriteriaControlBase control = CriteriaFactory.CreateControl(ind, CurrentFolderType.Identifier, !IsSearchMode, IsSearchMode);

                if (IsSearchMode)
                {
                    control.AcceptSpecialCharsForRegularExpression = true;
                    control.DisableMandatoryLabel();
                }
                else
                {
                    control.SetMaxLength();

                    //For list flow only, we attach an event to generate the timestamp number
                    if (ind.Type == (int) IndexDomainServiceEnums.IndexType.TypeFlow)
                    {
                        ((ListOjectIndexControl) control).SelectionChanged += IndexTypeFlow_SelectionChanged;
                    }
                }

                control.FilterClick += SearchPanel_FilterClick;

                _internalControlsList.Add(control);

                //Only added if it visible
                if (ind.IsVisible)
                {
                    StackFolderControl.Children.Add(control);
                }
            }
        }

        /// <summary>
        ///   Builds the control criterias with value.
        /// </summary>
        private void BuildControlCriteriasWithValue(List<string> values)
        {
            Clear();

            //Build and add each controls on the stack
            for (int i = 0; i < _internalListIndex.Count; i++)
            {
                var index = _internalListIndex.ElementAt(i);
                if (values != null && values.Count > 0)
                {
                    string value = string.Empty;
                    if (i <= values.Count)
                        value = values.ElementAt(i);

                    CriteriaControlBase control = CriteriaFactory.CreateControlWithValue(index, CurrentFolderType.Identifier, value, !IsSearchMode, IsSearchMode);

                    if (IsSearchMode)
                    {
                        control.AcceptSpecialCharsForRegularExpression = true;
                        control.DisableMandatoryLabel();
                    }
                    else
                    {
                        control.SetMaxLength();

                        //For list flow only, we attach an event to generate the timestamp number
                        //In search mode should not be overwrited
                        if (index.Type == (int) IndexDomainServiceEnums.IndexType.TypeFlow && UseChronoGeneration)
                        {
                            ((ListOjectIndexControl) control).SelectionChanged += IndexTypeFlow_SelectionChanged;
                        }
                    }

                    control.FilterClick += SearchPanel_FilterClick;

                    _internalControlsList.Add(control);

                    //Only added if it visible
                    if (index.IsVisible)
                    {
                        StackFolderControl.Children.Add(control);
                    }
                }
            }
        }

        /// <summary>
        ///   Initializes the control internal.
        /// </summary>
        private void InitializeControlInternal()
        {
            InitializeControl(CurrentOrganization, CurrentFolderType);

            OnIndexLoaded();
        }

        /// <summary>
        ///   Initializes the control with folder internal.
        /// </summary>
        private void InitializeControlWithFolderInternal()
        {
            if (CurrentFolder != null)
            {
                InitializeControl(CurrentOrganization, CurrentFolderType, new List<string>(CurrentFolder.ListValues));
            }
        }

        /// <summary>
        ///   Called when [generate chrono query].
        /// </summary>
        /// <param name="result"> The result. </param>
        private void OnGenerateChronoQuery(LoadOperation<Item> result)
        {
            try
            {
                IsEnabled = true;

                List<Item> l_oReturnItems = new List<Item>(result.Entities);
                if (l_oReturnItems.Count > 0 && !result.HasError)
                {
                    Item l_oReturnItem = l_oReturnItems.First();

                    if (l_oReturnItem.IsValid)
                    {
                        foreach (CriteriaControlBase item in _internalControlsList)
                        {
                            if (item.CurrentIndex.Type == (int) IndexDomainServiceEnums.IndexType.TypeChrono)
                            {
                                item.InputValue = l_oReturnItem.Name;
                                break;
                            }
                        }

                        RaiseChronoNumberGeneratedEvent(new ChronoNumberGeneratedEventArgs(l_oReturnItem.Identifier, l_oReturnItem.Name));
                    }
                    else
                    {
                        MessageBoxChildWindow L_oMessage = new MessageBoxChildWindow(String.Empty, "Erreur lors de la génération du Chrono /n" + l_oReturnItem.ErrorMessage, MessageBoxButtons.Ok,
                                                                                     MessageBoxIcon.Error);
                        L_oMessage.Show();
                    }
                }
                else
                {
                    MessageBoxChildWindow L_oMessage = new MessageBoxChildWindow(String.Empty, "Erreur lors de la génération du Chrono /n" + result.Error.Message, MessageBoxButtons.Ok,
                                                                                 MessageBoxIcon.Error);
                    L_oMessage.Show();
                }
            }
            catch (Exception ex)
            {
                MessageBoxChildWindow L_oMessage = new MessageBoxChildWindow(String.Empty, "----OnGenerateChronoQuery: " + ex.Message, MessageBoxButtons.Ok, MessageBoxIcon.Error);
                L_oMessage.Show();
            }
        }

        #endregion

        #region Event Handling

        /// <summary>
        ///   Handles the FilterClick event of the SearchPanel control.
        /// </summary>
        /// <param name="sender"> The source of the event. </param>
        /// <param name="e"> The <see cref="Rio.Framework.Pages.Events.FilterClickEventArgs" /> instance containing the event data. </param>
        protected void SearchPanel_FilterClick(object sender, FilterClickEventArgs e)
        {
            if (!_isFilterEnabled)
            {
                _isFilterEnabled = true;
                Dictionary<long, string> l_oDicIndexValues = GetIndexIdValues();

                // if no value inputed 
                //Not run when a boolean field is in the list => value 0,1
                bool Filtered =
                    Convert.ToBoolean(
                        l_oDicIndexValues.Where(p => p.Value != null && !p.Value.Trim().Equals(string.Empty)).Count());

                FilterWindow L_oFilterWindow = new FilterWindow(e.CurrentIndex, CurrentOrganization.Identifier,
                                                                CurrentFolderType.Identifier, l_oDicIndexValues,
                                                                FilterWindow.SelectedMode.Single, Filtered) {Header = e.CurrentIndex.IndexLabel};
                L_oFilterWindow.Closed += delegate
                                              {
                                                  //if a value has been selected
                                                  if (L_oFilterWindow.DialogResult == true)
                                                  {
                                                      ((CriteriaControlBase) sender).InputValue =
                                                          L_oFilterWindow.m_szSelectedItem;
                                                      ((CriteriaControlBase) sender).SetFocus();
                                                  }
                                                  _isFilterEnabled = false;
                                              };
                L_oFilterWindow.ShowDialog();
            }
        }

        /// <summary>
        ///   Handles the SelectionChanged event of the l_oIndexTypeFlow control.
        /// </summary>
        /// <param name="sender"> The source of the event. </param>
        /// <param name="e"> The <see cref="System.EventArgs" /> instance containing the event data. </param>
        private void IndexTypeFlow_SelectionChanged(object sender, EventArgs e)
        {
            CriteriaControlBase l_oIndTypeFlow = (CriteriaControlBase) sender;

            // We book the Chrono ID for that user 0-Fix; 1-Temporary; 2-Book for the current user
            BusinessEntitiesDomainContext business = new BusinessEntitiesDomainContext();
            var query = business.GenerateChronoQuery(CurrentFolderType.Identifier, l_oIndTypeFlow.InputValue);
            business.Load(query, OnGenerateChronoQuery, null);

            IsEnabled = false;
        }

        #endregion

        #region Event Declarations

        public event ChronoNumberGeneratedEventHandler ChronoNumberGenerated;
        public event EventHandler IndexLoaded;

        #endregion

        #region Class Methods

        /// <summary>
        ///   Called when [folder changed].
        /// </summary>
        /// <param name="d"> The d. </param>
        /// <param name="e"> The <see cref="System.Windows.DependencyPropertyChangedEventArgs" /> instance containing the event data. </param>
        private static void OnFolderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
            {
                StackIndexControl context = d as StackIndexControl;
                if (context != null) context.InitializeControlWithFolderInternal();
            }
        }

        /// <summary>
        ///   Called when [folderType changed].
        /// </summary>
        /// <param name="d"> The d. </param>
        /// <param name="e"> The <see cref="System.Windows.DependencyPropertyChangedEventArgs" /> instance containing the event data. </param>
        private static void OnFolderTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            StackIndexControl context = d as StackIndexControl;
            if (e.NewValue != null)
            {
                if (context != null) context.InitializeControlInternal();
            }
            else
            {
                if (context != null) context.Clear();
            }
        }

        /// <summary>
        ///   Called when [organization changed].
        /// </summary>
        /// <param name="d"> The d. </param>
        /// <param name="e"> The <see cref="System.Windows.DependencyPropertyChangedEventArgs" /> instance containing the event data. </param>
        private static void OnOrganizationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            StackIndexControl context = d as StackIndexControl;
            if (context != null) context.Clear();
        }

        #endregion

        #region Nested type: ChronoNumberGeneratedEventArgs

        public class ChronoNumberGeneratedEventArgs
            : EventArgs
        {
            #region Constructors

            public ChronoNumberGeneratedEventArgs(long chronoId, string chronoName)
            {
                ChronoId = chronoId;
                ChronoName = chronoName;
            }

            #endregion

            #region Instance Properties

            public long ChronoId { get; set; }
            public string ChronoName { get; set; }

            #endregion
        }

        #endregion
    }
}